
package com.hlkj.pay.app.merchant.ext.impl.wxpay.ext;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.WxScenePayOrderExtPt;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.constant.WxPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.dto.WxAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.dto.WxPayConfig;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.handler.WxPayCommonParamsHandle;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.util.MerchantUtils;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.SettleInfo;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jodd.util.StringPool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * JSAPI统一下单
 * 
 * @author HlpayTeam
 * @date 2024/11/18 13:07
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = WxPayExtConstant.JSAPI_PAY)
public class WxJsApiPayOrderExtPt extends WxSceneCommonPayOrder implements WxScenePayOrderExtPt {

    private final WxPayCommonParamsHandle wxPayCommonParamsHandle;

    @Value("${hlkj.domain}")
    private String domain;


    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {

        log.info("微信JSAPI支付开始请求参数:{}", JsonUtils.toJsonString(channelPayOrderContext));
        // 判断商户类型
        MerchantAppChannelDO merchantAppChannelDO = channelPayOrderContext.getMerchantAppChannelDO();
        boolean common = false;
        if(merchantAppChannelDO.getType() != null && MerchantEnum.APP_CHANNEL_TYPE.MERCHANT_COMMON.getCode()  == merchantAppChannelDO.getType()){
            common = true;
        }
        // 微信配置信息
        WxPayConfig wxPayConfig = null;
        // 普通商户
        if(common){
            wxPayConfig = JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), WxPayConfig.class);
            if(wxPayConfig == null){
                channelPayOrderContext.setRemark("普通商户未配置通道参数");
                channelPayOrderContext.setProcessStatus(false);
                return;
            }
        }else{
            CommonResult<WxPayConfig> wxPayConfigCommonResult = wxPayCommonParamsHandle.wxPayConfig(channelPayOrderContext.getProviderChannelConfigDO(),
                    channelPayOrderContext.isTest());
            if (!wxPayConfigCommonResult.isSuccess()) {
                channelPayOrderContext.setRemark(wxPayConfigCommonResult.getMsg());
                channelPayOrderContext.setProcessStatus(false);
                return;
            }
            wxPayConfig = wxPayConfigCommonResult.getData();
        }

        PayOrderDO payOrderDO = channelPayOrderContext.getPayOrderDO();
        // 额外参数信息
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        // userId 判断
        if(StrUtil.isEmpty(extra.getUserId())){
            channelPayOrderContext.setRemark("微信JSAPI支付userId必填");
            channelPayOrderContext.setProcessStatus(false);
            return;
        }
        try {
            // 请求地址
            String url = null;
            // 请求参数封装
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setDescription(payOrderDO.getDescription())
                    .setOut_trade_no(payOrderDO.getPayOrderNo())
                    .setNotify_url(channelPayOrderContext.getNotifyUrl())
                    .setSettle_info(new SettleInfo().setProfit_sharing(true))
                    .setAmount(new Amount().setTotal(MerchantUtils.YuanToFen(payOrderDO.getAmount())).setCurrency("CNY"));

            // 普通商户
            if(common){
                if(StrUtil.isEmpty(wxPayConfig.getWxPubAppid())){
                    channelPayOrderContext.setRemark("普通商户公众号支付appid必填");
                    channelPayOrderContext.setProcessStatus(false);
                    return;
                }
                // 商户回调通知
                String notifyUrl = domain + MerchantExtConstant.MERCHANT_TRANS_COMMON_ASYNC_CALLBACK_URL + payOrderDO.getChannelCode() + StringPool.SLASH + payOrderDO.getProviderCode()+ StringPool.SLASH + merchantAppChannelDO.getCode();
                unifiedOrderModel.setAppid(wxPayConfig.getWxPubAppid()).setMchid(wxPayConfig.getMchId()).setNotify_url(notifyUrl).setPayer(new Payer().setOpenid(extra.getUserId()) );
                url = BasePayApiEnum.JS_API_PAY.toString();
                log.info("普通商户微信JSAPI支付请求参数:{}", JSONUtil.toJsonStr(unifiedOrderModel));
            }else{
                // 服务商
                WxAppChannelParams WxAppChannelParams = JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), WxAppChannelParams.class);
                unifiedOrderModel.setSp_appid(extra.getSubAppid()).setSp_mchid(wxPayConfig.getMchId())
                        .setSub_mchid(WxAppChannelParams.getSubMchId()).setNotify_url(channelPayOrderContext.getNotifyUrl())
                        .setPayer(new Payer().setSub_openid(extra.getUserId()) );;
                url = BasePayApiEnum.PARTNER_JS_API_PAY.toString();

                // 分账  优先接口传值没有就去配置取
                if(payOrderDO.getFundProcessType() != null && payOrderDO.getFundProcessType() != 0){
                    unifiedOrderModel.setSettle_info(new SettleInfo().setProfit_sharing(true));
                }else{
                    if(merchantAppChannelDO.getFundProcessType() != null && payOrderDO.getFundProcessType() != 0){
                        unifiedOrderModel.setSettle_info(new SettleInfo().setProfit_sharing(true));
                    }
                }
                log.info("服务商微信JSAPI支付请求参数:{}", JSONUtil.toJsonStr(unifiedOrderModel));
            }

            // 响应参数
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    url,
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    wxPayConfig.getPayPublicId(),
                    wxPayConfig.getPrivateKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel),
                    AuthTypeEnum.RSA.getCode()
            );
            log.info("微信JSAPI支付返回参数:{}", response);
            if (response.getStatus() == WxPayExtConstant.OK) {
                // 根据支付公钥查询对应的证书来验证签名结果
                boolean verifySignature = WxPayKit.verifyPublicKeySignature(response, wxPayConfig.getPayPublicPath());
                if(!verifySignature){
                    channelPayOrderContext.setProcessStatus(Boolean.FALSE);
                    channelPayOrderContext.setRemark("微信支付公钥验签失败");
                    return;
                }
                JSONObject resp = JSONUtil.parseObj(response.getBody());
                channelPayOrderContext.setPayDataType("data");
                /**
                 * 小程序需要将prepay_id 转成已签名的json
                 * {
                 *     "timeStamp": "1414561699",
                 *     "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
                 *     "package": "prepay_id=wx201410272009395522657a690389285100",
                 *     "signType": "RSA",
                 *     "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",
                 * }
                 */
                Map<String,String> prepayReqMap = null;
                if(common){
                    prepayReqMap = WxPayKit.jsApiCreateSign(wxPayConfig.getWxPubAppid(),resp.getStr("prepay_id"),wxPayConfig.getPrivateKeyPath());
                }else{
                    prepayReqMap = WxPayKit.jsApiCreateSign(extra.getSubAppid(),resp.getStr("prepay_id"),wxPayConfig.getPrivateKeyPath());
                }

                if(MapUtil.isNotEmpty(prepayReqMap)){
                    channelPayOrderContext.setPayInfo(JSONUtil.toJsonStr(prepayReqMap));
                }else{
                    channelPayOrderContext.setProcessStatus(Boolean.FALSE);
                    channelPayOrderContext.setRemark("微信提交JSAPI支付 prepay_id 创建签名错误,prepay_id= "+resp.getStr("prepay_id"));
                }
            }else{
                channelPayOrderContext.setProcessStatus(Boolean.FALSE);
                channelPayOrderContext.setRemark(response.getBody());
            }

        }catch (Exception e) {
            log.error("微信支付提交JSAPI支付请求失败 message:{}",  e);
            channelPayOrderContext.setRemark("微信支付提交JSAPI支付请求失败,失败信息" + e.getMessage());
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        commonQueryChannelPayOrder(channelPayOrderQueryContext);
    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        commonCloseChannelPayOrder(channelPayOrderQueryContext);
    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        commonQueryChannelClosePayOrder(channelPayOrderQueryContext);
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        commonRefundChannelPayOrder(channelRefundPayOrderContext);
    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        commonQueryRefundChannelPayOrder(channelRefundPayOrderContext);
    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        return true;
    }

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }
}
